#include "turn.hpp"
#include "dirs.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <memory>

namespace Trade {

time_t
TimeTurn::fact_dir(const dir_t d) const noexcept
{
  return (d > 0) ? t_up.t : t_dn.t;
}

bool
TimeTurn::to_up(const time_t tm) noexcept
{
  t_up.is_timer_outed(tm);
  return t_up.to;
}

bool
TimeTurn::to_dn(const time_t tm) noexcept
{
  t_dn.is_timer_outed(tm);
  return t_dn.to;
}

void
TimeTurn::print(const int64_t idx,
                const time_t ts,
                const price_t hpx) const noexcept
{

#if ONLINE_DATA_COMM
  printf("%c %d %.02lf -- U%d D%d g%ld g%ld -- g%ld %.02lf (%.02lf)\n",
         ((im_large) ? '&' : ' '),
         dir,
         hp_delta,
         int(t_up.t - ts) / 60,
         int(t_dn.t - ts) / 60,
         t_up.t,
         t_dn.t,
         my_ts,
         my_hp,
         (hpx - my_hp));
#else
  printf("%c %d %.02lf -- U%d D%d g%ld g%ld -- g%ld %.02lf (%.02lf)\n",
         ((im_large) ? '&' : ' '),
         dir,
         hp_delta,
         int(t_up.t - ts) / 60,
         int(t_dn.t - ts) / 60,
         idx + (t_up.t - ts),
         idx + (t_dn.t - ts),
         my_idx,
         my_hp,
         (hpx - my_hp));
#endif // ONLINE
}

uint8_t
TimeTurn::check_reached(const time_t tm) noexcept
{
  uint8_t to_up = t_up.is_timer_outed(tm);
  uint8_t to_dn = t_dn.is_timer_outed(tm);

  return (to_up | to_dn);
}

bool
TimeTurn::strict_dir(const dir_t d) const noexcept
{
  return (dir == d && sign(hp_delta) == d);
}

bool
TimeTurn::good_dir(const dir_t d) const noexcept
{
  return (dir == d);
}

dir_t
TimeTurn::strong_one_dir(const time_t ts) const noexcept
{
  // 3 HOURS - 15 Minutes
  dir_t d = 0;
  //  45 ~ 40
  if (((t_up.t - ts > (180 - 15) * 60) && (t_dn.t - ts < 40 * 60)) ||
      (t_up.t - ts >= 40 * 60 && t_dn.t < ts))
    d = 1;
  else if (((t_up.t - ts < 40 * 60) && (t_dn.t - ts > (180 - 15) * 60)) ||
           (t_up.t < ts && t_dn.t - ts >= 40))
    d = -1;
  return d;
}

bool
TimeTurn::large_time(const dir_t d, const time_t ts) const noexcept
{
  auto tt = (d > 0) ? t_up.t : t_dn.t;
  auto ot = (d > 0) ? t_dn.t : t_up.t;

  int td = int(tt - ts) / 60;
  int od = int(ot - ts) / 60;

  auto yes = (td > 180 && od < 60);
  return yes;
}

bool
TimeTurn::i_am_important(const price_t hpx) const noexcept
{
  return (im_large || ((hpx - my_hp) * dir > 12.1));
}

bool
TimeTurn::more_than(const time_t ts,
                    const dir_t d,
                    const int minutes) const noexcept
{
  return ((d > 0) && (t_up.t - ts > (minutes - 3) * 60)) ||
         ((d < 0) && (t_dn.t - ts > (minutes - 3) * 60));
}

bool
Turns::is_timer_outed(const dir_t d, const time_t ts) const noexcept
{
  if (!tt_up || !tt_dn)
    return false;

  timer_out(d, ts);
  bool to = (d > 0) ? tt_up->t_up.to : tt_dn->t_dn.to;

  return to;
}

bool
Turns::new_turn(const dir_t d,
                const int64_t idx,
                const time_t ts,
                const price_t hpx,
                const time_t tup,
                const time_t tdn) noexcept
{
  auto turn = std::make_shared<TimeTurn>(d, idx, ts, hpx, tup, tdn);
  if (!tl.empty()) {
    auto prev = tl.front();
    turn->hp_delta = hpx - prev->my_hp;
    turn->t_delta = int(ts - prev->my_ts) / 60;
    turn->im_large = std::abs(turn->hp_delta) > MIN_TURN_LARGE_DELTA;
  }

  while (int(tl.size()) >= _deep)
    tl.pop_back();
  tl.push_front(turn);

  if (turn->im_large) {
    init_large_turn(turn, ts);
    check_turn_time_out(ts);
    tt_cur_large = turn;
  }

  return true;
}

dir_t
Turns::up_to_now(const time_t tm_now,
                 const price_t hp_now,
                 price_t& large_to_new,
                 price_t& large_to_new_with_large,
                 price_t& oppo_to_new,
                 price_t& oppo_to_new_with_large,
                 price_t& delta_first_to_now) const noexcept
{
  if (tl.size() < 10)
    return (0);

  auto i = tl.cbegin();
  for (; i != tl.cend(); ++i) {
    if ((*i) == tt_cur_large) {
      large_to_new_with_large = large_to_new + (*i)->hp_delta;
      break;
    } else {
      large_to_new += (*i)->hp_delta;
    }
  }
  delta_first_to_now = hp_now - (*(tl.cbegin()))->my_hp;
  if (i != tl.cend()) {
    auto i2 = i;
    ++i2;
    for (; i2 != tl.cend(); ++i2) {
      if ((*i2)->im_large && (*i2)->dir == tt_cur_large->dir) {
        break;
      }
    }
    if (i2 != tl.cend()) {
      ++i;
      oppo_to_new = large_to_new;
      for (; i != i2; ++i) {
        oppo_to_new += (*i)->hp_delta;
      }
      oppo_to_new_with_large = oppo_to_new + (*i)->hp_delta;
    }
  }
  return sign(large_to_new_with_large);
}

void
Turns::init_large_turn(const TimeTurnPtr& turn, const time_t ts) noexcept
{
  if (!tt_up) {
    tt_up = turn;
  }
  if (!tt_dn) {
    tt_dn = turn;
  }
}

bool
Turns::change_turn(const time_t ts) noexcept
{
  auto find_ptr = [this](const TimeTurnPtr& ttp) -> CttlIterator {
    auto i = tl.cbegin();
    for (; i != tl.cend(); ++i)
      if ((*i) == ttp)
        break;
    return i;
  };

  auto find_next_ptr = [this, ts](const dir_t d,
                                  CttlIterator& it) -> CttlIterator {
    auto i = it;
    if (i == tl.cbegin())
      return tl.cend();

    --i;
    while (true) {
      if ((*i)->dir == d && (*i)->im_large &&
          (((*i)->t_up.t > ts && (*it)->dir > 0) ||
           ((*i)->t_dn.t > ts && (*it)->dir < 0)))
        return i;
      if (i == tl.cbegin())
        break;
      --i;
    }

    return tl.cend();
  };

  auto change = [&](const dir_t d, TimeTurnPtr& tt) -> uint8_t {
    auto me = find_ptr(tt);
    if (me != tl.cend()) {
      auto new_me = find_next_ptr(d, me);
      if (new_me != tl.cend()) {
        tt = *new_me;
        return true;
      }
    }

    return false;
  };

  bool changed_up = false;
  if (tt_up) {
    if (tt_up->is_timer_outed(ts)) {
      // TIMER OUT
      changed_up = change(1, tt_up);
    }
  }

  bool changed_dn = false;
  if (tt_dn) {
    if (tt_dn->is_timer_outed(ts)) {
      // TIMER OUT
      changed_dn = change(-1, tt_dn);
    }
  }

  return (changed_up or changed_dn);
}

std::pair<uint8_t, uint8_t>
Turns::check_turn_time_out(const time_t ts) noexcept
{
  uint8_t to_up = 0, to_dn = 0;
  if (tt_up)
    to_up = tt_up->to_up(ts);
  if (tt_dn)
    to_dn = tt_dn->to_dn(ts);
  return std::pair<uint8_t, uint8_t>(to_up, to_dn);
}

bool
Turns::timer_out(const dir_t d, const time_t ts) const noexcept
{
  auto to = (d > 0) ? tt_up->to_up(ts) : tt_dn->to_dn(ts);
  return to;
}

void
Turns::print(const int64_t idx,
             const int64_t ts,
             const price_t hpx) const noexcept
{
  if (tt_up) {
    auto to_up = is_timer_outed(1, ts);
    printf("TUP to %d: ", to_up);
    tt_up->print(idx, ts, hpx);
  }
  if (tt_dn) {
    auto to_dn = is_timer_outed(-1, ts);
    printf("TDN to %d: ", to_dn);
    tt_dn->print(idx, ts, hpx);
  }

  int i = 0;
  for (auto it = tl.cbegin(); i < 3 && it != tl.cend(); ++it, ++i) {
    (*it)->print(idx, ts, hpx);
  }
}

case_t
Turns::get_time_out_case(const time_t t_now, const dir_t d) const noexcept
{
  const bool large_turn_up_to = is_timer_outed(1, t_now);
  const bool large_turn_dn_to = is_timer_outed(-1, t_now);
  auto turn = first();
  const bool small_turn_up_to = first()->t_up.is_timer_outed(t_now);
  const bool small_turn_dn_to = first()->t_dn.is_timer_outed(t_now);

  bool lt_this_to = false;
  bool lt_oppo_to = false;
  bool st_this_to = false;
  bool st_oppo_to = false;

  if (d > 0) {
    lt_this_to = large_turn_up_to;
    lt_oppo_to = large_turn_dn_to;
    st_this_to = small_turn_up_to;
    st_oppo_to = small_turn_dn_to;
  } else {
    lt_this_to = large_turn_up_to;
    lt_oppo_to = large_turn_dn_to;
    st_this_to = small_turn_up_to;
    st_oppo_to = small_turn_dn_to;
  }

  case_t tcase = 0;
  if (lt_this_to && st_this_to) {
    tcase = 10;
  } else if (lt_this_to && !st_this_to) {
    tcase = 20;
  } else if (!lt_this_to && st_this_to) {
    tcase = 30;
  } else {
    tcase = 40;
  }

  if (lt_oppo_to && st_oppo_to) {
    tcase += +1;
  } else if (lt_oppo_to && !st_oppo_to) {
    tcase += +2;
  } else if (!lt_oppo_to && st_oppo_to) {
    tcase += +3;
  } else {
    tcase += +4;
  }

  return tcase;
}

time_t
Turns::get_strong_timer(const dir_t d, const time_t t_now) noexcept
{
  if (!tt_up || !tt_dn || !first())
    return (0);

  time_t t = 0;

  if (d > 0) {
    if (tt_up->t_up.is_timer_outed(t_now)) {
      if (first()->dir == d) {
        // Front->up
        if (!first()->t_up.is_timer_outed(t_now)) {
          t = first()->t_up.t;
        }
      } else {
        if (tt_dn->t_up.is_timer_outed(t_now)) {
        } else {
          // TL-DN->up
          t = tt_dn->t_up.t;
        }
      }
    } else {
      // TL-UP->up
      t = tt_up->t_up.t;
    }
  } else {
    if (tt_dn->t_dn.is_timer_outed(t_now)) {
      if (first()->dir == d) {
        // Front->dn
        if (!first()->t_dn.is_timer_outed(t_now)) {
          t = first()->t_dn.t;
        }
      } else {
        if (tt_up->t_dn.is_timer_outed(t_now)) {
        } else {
          // TL-UP->dn
          t = tt_up->t_dn.t;
        }
      }
    } else {
      // TL-DN->dn
      t = tt_dn->t_dn.t;
    }
  }
  return t;
}

time_t
Turns::get_weak_timer(const dir_t d, const time_t t_now) noexcept
{
  if (!tt_up || !tt_dn || !first())
    return (0);

  time_t t = 0;

  if (d > 0) {
    if (first()->dir == -d) {
      // Front->up
      if (!first()->t_up.is_timer_outed(t_now)) {
        t = first()->t_up.t;
      }
    } else {
      auto sec = second();
      if (sec && sec->dir == -d) {
        // Second->up
        if (!sec->t_up.is_timer_outed(t_now)) {
          t = sec->t_up.t;
        }
      } else if (!tt_dn->t_up.is_timer_outed(t_now)) {
        // TL-UP->up
        t = tt_dn->t_up.t;
      }
    }

  } else {
    if (first()->dir == -d) {
      // Front->dn
      if (!first()->t_dn.is_timer_outed(t_now)) {
        t = first()->t_dn.t;
      }
    } else {
      auto sec = second();
      if (sec && sec->dir == -d) {
        // Second->dn
        if (!sec->t_dn.is_timer_outed(t_now)) {
          t = sec->t_dn.t;
        }
      } else if (tt_up->t_dn.is_timer_outed(t_now)) {
        // TL-UP->dn
        t = tt_up->t_dn.t;
      }
    }
  }

  return t;
}

bool
Turns::very_good() const noexcept
{
  return (first() && second() && tt_up && tt_dn);
}

const int MIN_30 = 30;
const int MIN_45 = 45;
const int MIN_90 = 90;
const int MIN_120 = 120;

// return true if the status was changed
bool
Turns::good_time_changed(const time_t ts, const dir_t d) noexcept
{
  TimeGood good;

  auto& t_status = (d > 0) ? good.ts_up : good.ts_dn;

  if (all_to(ts, 1) && all_no_to(ts, -1) && d < 0) {
    t_status.cno = 5;
  } else if (all_no_to(ts, 1) && all_to(ts, -1) && d > 0) {
    t_status.cno = 5;
  } else {
    const auto& tt = (d > 0) ? tt_up : tt_dn;
    const auto& near = (first()->dir == d) ? first() : second();
    const auto& near_oppo = (first()->dir == d) ? second() : first();

    if (tt->more_than(ts, d, MIN_30) && first()->more_than(ts, d, MIN_30) &&
        second()->more_than(ts, d, MIN_30)) {
      if (tt->more_than(ts, d, MIN_90) || near->more_than(ts, d, MIN_90)) {
        t_status.cno = 1;
      } else if (tt->more_than(ts, d, MIN_45 - 2) ||
                 near->more_than(ts, d, MIN_45 - 2)) {
        t_status.cno = 2;
      }
    } else if (tt->more_than(ts, d, MIN_30 - 1) &&
               first()->more_than(ts, d, MIN_30 - 1) &&
               second()->more_than(ts, d, MIN_30 - 1)) {
      if (tt->more_than(ts, d, MIN_120) || near->more_than(ts, d, MIN_120)) {
        t_status.cno = 3;
      } else {
        t_status.cno = 4;
      }
    }
  }
  t_status.good = (t_status.cno != 0);

  auto& old_status = (d > 0) ? tg.ts_up : tg.ts_dn;
  if (old_status.good != t_status.good) {
    old_status.good = t_status.good;
    old_status.cno = t_status.cno;
    return true;
  }

  return false;
}

time_t
Turns::get_long_turn_timer(const dir_t d) noexcept
{
  const auto& tt = (d > 0) ? tt_up : tt_dn;
  return (d > 0) ? tt->t_up.t : tt->t_dn.t;
}

time_t
Turns::get_long_oppo_turn_timer(const dir_t d) noexcept
{
  const auto& tt = (d > 0) ? tt_dn : tt_up;
  return (d > 0) ? tt->t_up.t : tt->t_dn.t;
}

time_t
Turns::get_near_turn_timer(const dir_t d) noexcept
{
  const auto& near = (first()->dir == d) ? first() : second();
  return (d > 0) ? near->t_up.t : near->t_dn.t;
}

time_t
Turns::get_near_oppo_turn_timer(const dir_t d) noexcept
{
  const auto& near_oppo = (first()->dir == d) ? second() : first();
  return (d > 0) ? near_oppo->t_up.t : near_oppo->t_dn.t;
}

bool
Turns::get_long_turn_in_time_range(const dir_t d) noexcept
{
  bool yes = false;
  return yes;
}

bool
Turns::get_long_oppo_turn_in_time_range(const dir_t d) noexcept
{
  bool yes = false;
  return yes;
}

bool
Turns::get_near_turn_in_time_range(const dir_t d) noexcept
{
  bool yes = false;
  return yes;
}

bool
Turns::get_near_oppo_turn_in_time_range(const dir_t d) noexcept
{
  bool yes = false;
  return yes;
}

bool
Turns::turn_time_status_changed(const time_t ts,
                                const dir_t big_reverse) noexcept
{
  if (!very_good())
    return false;

  TURN_TIME_STATE s = TURN_TIME_STATE::UNKONW;

  good_time_changed(ts, 1);
  good_time_changed(ts, -1);
  add_big_reverse_needle_status(big_reverse);

  s = retrive_turn_time_status();

  if (tts != s) {
    tts = s;

#if (STONE_DEBUG)
    printf(
      "Time UP %d, DN %d --> %u\n", tg.ts_up.good, tg.ts_dn.good, uint8_t(s));
#endif // debug

    return true;
  }
  return false;
}

TURN_TIME_STATE
Turns::retrive_turn_time_status() const noexcept
{
  TURN_TIME_STATE tts = TURN_TIME_STATE::UNKONW;
  if (tg.ts_up.good && !tg.ts_dn.good)
    tts = TURN_TIME_STATE::UP;
  else if (!tg.ts_up.good && tg.ts_dn.good)
    tts = TURN_TIME_STATE::DN;
  else if (tg.ts_up.good && tg.ts_dn.good)
    tts = TURN_TIME_STATE::BOTH_GOOD;
  else {
    tts = TURN_TIME_STATE::BOTH_ZERO;
  }
  return tts;
}

TURN_TIME_STATE
Turns::get_turn_time_status() const noexcept
{
  return tts;
}

dir_t
Turns::get_timer_dir() const noexcept
{
  dir_t d = 0;
  const TURN_TIME_STATE tts = get_turn_time_status();
  switch (tts) {
    case TURN_TIME_STATE::UP:
      d = 1;
      break;
    case TURN_TIME_STATE::DN:
      d = -1;
      break;
    case TURN_TIME_STATE::BOTH_GOOD:
      d = -10;
      break;
    case Trade::TURN_TIME_STATE::BOTH_ZERO:
    default:
      break;
  }
  return d;
}

bool
Turns::near_turn_is_good(const time_t ts, const dir_t d) const noexcept
{
  const auto& near = (first()->dir == d) ? first() : second();
  const auto& near_oppo = (first()->dir == d) ? second() : first();
  bool yes =
    (near->more_than(ts, d, MIN_45)) and (near_oppo->more_than(ts, d, MIN_30));
  return yes;
}

bool
Turns::all_to(const time_t ts, const dir_t d) const noexcept
{
  bool yes = true;
  if (d > 0) {
    yes = tt_up->t_up.is_timer_outed(ts);
    if (yes)
      yes = tt_dn->t_up.is_timer_outed(ts);
    if (yes)
      yes = first()->t_up.is_timer_outed(ts);
    if (yes)
      yes = second()->t_up.is_timer_outed(ts);
  } else {
    yes = tt_up->t_dn.is_timer_outed(ts);
    if (yes)
      yes = tt_dn->t_dn.is_timer_outed(ts);
    if (yes)
      yes = first()->t_dn.is_timer_outed(ts);
    if (yes)
      yes = second()->t_dn.is_timer_outed(ts);
  }
  return yes;
}

bool
Turns::all_no_to(const time_t ts, const dir_t d) const noexcept
{
  bool yes = false;
  if (d > 0) {
    yes = tt_up->t_up.is_timer_outed(ts);
    if (!yes)
      yes = tt_dn->t_up.is_timer_outed(ts);
    if (!yes)
      yes = first()->t_up.is_timer_outed(ts);
    if (!yes)
      yes = second()->t_up.is_timer_outed(ts);
  } else {
    yes = tt_up->t_dn.is_timer_outed(ts);
    if (!yes)
      yes = tt_dn->t_dn.is_timer_outed(ts);
    if (!yes)
      yes = first()->t_dn.is_timer_outed(ts);
    if (!yes)
      yes = second()->t_dn.is_timer_outed(ts);
  }
  return yes;
}

time_t
Turns::get_minimam_timer(const dir_t d, const time_t t_now) noexcept
{
  time_t t = 0;
  time_t t1 = 0, t2 = 0, t3 = 0, t4 = 0;
  if (d > 0) {
    t1 = tt_up->t_up.t;
    t2 = tt_dn->t_up.t;
    t3 = first()->t_up.t;
    t4 = second()->t_up.t;
  } else {
    t1 = tt_up->t_dn.t;
    t2 = tt_dn->t_dn.t;
    t3 = first()->t_dn.t;
    t4 = second()->t_dn.t;
  }

  if ((t1 > t_now) || (t2 > t_now) || (t3 > t_now) || (t4 > t_now)) {

  } else {
    // All Time Outed
    // t = 0;
  }

  if (!t)
    t = MINUTES_45 + t_now;

  return t;
}

bool
Turns::add_big_reverse_needle_status(const dir_t d) noexcept
{
  if (!d)
    return false;

  bool yes = false;
  if (d < 0) {
    if (!tg.ts_up.good) {
      tg.ts_up.good = true;
      tg.ts_up.cno = 40;
      yes = true;
    } else {
      // tg.ts_up.cno += 40;
    }
  } else {
    if (!tg.ts_dn.good) {
      tg.ts_dn.good = true;
      tg.ts_dn.cno = 40;
      yes = true;
    } else {
      // tg.ts_dn.cno += 40;
    }
  }

  retrive_turn_time_status();
  return yes;
}

dir_t
Turns::space_dir(const time_t t_now, const price_t hp_now) const noexcept
{
  if (tl.size() < 10)
    return (0);

  price_t large_to_now
    [[maybe_unused]] = 0,
    large_to_now_with_large [[maybe_unused]] = 0, large_to_new = 0,
    large_to_new_with_large = 0, oppo_to_new [[maybe_unused]] = 0,
    oppo_to_new_with_large [[maybe_unused]] = 0,
    oppo_to_now [[maybe_unused]] = 0,
    oppo_to_now_with_large [[maybe_unused]] = 0, delta_first_to_now = 0;

  up_to_now(t_now,
            hp_now,
            large_to_new,
            large_to_new_with_large,
            oppo_to_new,
            oppo_to_new_with_large,
            delta_first_to_now);
  auto dir_large_to_now = sign(large_to_new + delta_first_to_now);
  auto dir_large_to_now_with_large =
    sign(large_to_new_with_large + delta_first_to_now);
  // oppo_to_now = oppo_to_new + delta_first_to_now;
  // oppo_to_now_with_large = oppo_to_new_with_large + delta_first_to_now;

  // if (tt_cur_large->dir == sign(tt_cur_large->hp_delta) &&
  //     tt_cur_large->dir == dir_large_to_now_with_large)
  //   d = dir_large_to_now_with_large;
  // else if (dir_large_to_now == dir_large_to_now_with_large &&
  //          tt_cur_large->dir == dir_large_to_now)
  //   d = dir_large_to_now;

  dir_t d = 0;
  if (dir_large_to_now == first()->dir &&
      dir_large_to_now == sign(first()->hp_delta) &&
      std::abs(large_to_now) > 19.9) {
    d = dir_large_to_now;
  } else {
    d = dir_large_to_now_with_large;
  }

  return d;
}

bool
Turns::most_weak_time(const time_t t_now) const noexcept
{
  if (tl.size() < 3)
    return false;
  auto i = tl.cbegin();
  if ((*i)->my_ts - t_now < MINUTES_30)
    return false;
  ++i;
  if ((*i)->my_ts - t_now < MINUTES_30)
    return false;
  ++i;
  if ((*i)->my_ts - t_now < MINUTES_30)
    return false;
  return true;
}

DIR_DREAMER_TYPE
Turns::get_first_dir_type() const noexcept
{
  DIR_DREAMER_TYPE ddt = DIR_DREAMER_TYPE::DD_UNKNOW;
  auto i = tl.cbegin();
  if (i != tl.cend()) {
    ddt = ((*i)->dir > 0) ? DIR_DREAMER_TYPE::DD_UP : DIR_DREAMER_TYPE::DD_DN;
  }
  return ddt;
}

void
Turns::conveter_to_5(Turns5& t5) const noexcept
{
  if (tt_up)
    t5.set_tt_up(tt_up.get());
  if (tt_dn)
    t5.set_tt_dn(tt_dn.get());
  auto i = tl.cbegin();
  if (i != tl.cend()) {
    t5.set_tt_1((*(i)).get());
    ++i;
    if (i != tl.cend()) {
      t5.set_tt_2((*(i)).get());
      ++i;
      if (i != tl.cend())
        t5.set_tt_3((*(i)).get());
    }
  }
}

void
Turns5::print(const int64_t idx,
              const int64_t ts,
              const price_t hpx) const noexcept
{
  tt_up.print(idx, ts, hpx);
  tt_dn.print(idx, ts, hpx);
  tt_1.print(idx, ts, hpx);
  tt_2.print(idx, ts, hpx);
  tt_3.print(idx, ts, hpx);
}

DIR_DREAMER_TYPE
Turns5::get_first_dir_type() const noexcept
{
  DIR_DREAMER_TYPE ddt = DIR_DREAMER_TYPE::DD_UNKNOW;
  if (tt_1.dir) {
    ddt = (tt_1.dir > 0) ? DIR_DREAMER_TYPE::DD_UP : DIR_DREAMER_TYPE::DD_DN;
  }
  return ddt;
}

time_t
Turns5::get_strong_timer(const dir_t d, const time_t t_now) noexcept
{
  if (!tt_up.dir || !tt_dn.dir || !tt_1.dir)
    return (0);

  time_t t = 0;

  if (d > 0) {
    if (tt_up.t_up.is_timer_outed(t_now)) {
      if (tt_1.dir == d) {
        // Front->up
        if (!tt_1.t_up.is_timer_outed(t_now)) {
          t = tt_1.t_up.t;
        }
      } else {
        if (tt_dn.t_up.is_timer_outed(t_now)) {
        } else {
          // TL-DN->up
          t = tt_dn.t_up.t;
        }
      }
    } else {
      // TL-UP->up
      t = tt_up.t_up.t;
    }
  } else {
    if (tt_dn.t_dn.is_timer_outed(t_now)) {
      if (tt_1.dir == d) {
        // Front->dn
        if (!tt_1.t_dn.is_timer_outed(t_now)) {
          t = tt_1.t_dn.t;
        }
      } else {
        if (tt_up.t_dn.is_timer_outed(t_now)) {
        } else {
          // TL-UP->dn
          t = tt_up.t_dn.t;
        }
      }
    } else {
      // TL-DN->dn
      t = tt_dn.t_dn.t;
    }
  }
  return t;
}

time_t
Turns5::get_weak_timer(const dir_t d, const time_t t_now) noexcept
{
  if (!tt_up.dir || tt_dn.dir || tt_1.dir)
    return (0);

  time_t t = 0;

  if (d > 0) {
    if (tt_1.dir == -d) {
      // Front->up
      if (!tt_1.t_up.is_timer_outed(t_now)) {
        t = tt_1.t_up.t;
      }
    } else {
      auto sec = tt_2;
      if (sec.dir == -d) {
        // Second->up
        if (!sec.t_up.is_timer_outed(t_now)) {
          t = sec.t_up.t;
        }
      } else if (!tt_dn.t_up.is_timer_outed(t_now)) {
        // TL-UP->up
        t = tt_dn.t_up.t;
      }
    }

  } else {
    if (tt_1.dir == -d) {
      // Front->dn
      if (!tt_1.t_dn.is_timer_outed(t_now)) {
        t = tt_1.t_dn.t;
      }
    } else {
      auto sec = tt_2;
      if (sec.dir == -d) {
        // Second->dn
        if (!sec.t_dn.is_timer_outed(t_now)) {
          t = sec.t_dn.t;
        }
      } else if (tt_up.t_dn.is_timer_outed(t_now)) {
        // TL-UP->dn
        t = tt_up.t_dn.t;
      }
    }
  }

  return t;
}

} // namespace Trade